#include "Strategy.h"
#include <QMap>
#include <functional>

Strategy::Strategy() {}

Strategy::Strategy(Player *player, const Cards &cards):m_player(player),m_cards(cards)
{}

Cards Strategy::makeStrategy()
{
    //得到出牌玩家以及打出的牌
    Player* pendPlayer = m_player->pendPlayer();
    Cards pendCards = m_player->pendCards();
    //判断上次出牌的玩家是否是自己，如果是则出牌无限制
    if(pendPlayer == m_player || pendPlayer == nullptr){
        //直接出牌（无限制）
        return firstPlay();
    }else{
        //如果不是我自己，找出比出牌玩家更大的牌
        PlayHand type(pendCards);
        Cards beatCards = getGreaterCards(type);
        //找到更大的牌，考虑是否要出牌
        bool shouldBeat = whetherToBeat(beatCards);
        if(shouldBeat){
            return beatCards;
        }else{
            return Cards();
        }
    }
    return Cards();
}

Cards Strategy::firstPlay()
{
    // 判断是否只有一个牌型了，出牌直接赢了
    PlayHand hand(m_cards);
    if(hand.handType() != PlayHand::Hand_Unknown){
        return m_cards;
    }
    // 还有很多牌，不是单一牌型
    //判断是否有顺子
    QVector<Cards> optimalSeq = pickOptimalSeqSingles();
    if(!optimalSeq.isEmpty()){
        //判断打出前后单牌数量是否增加，来作为是否合适的依据
        int baseNum = findCardsByCount(1).size();
        Cards save = m_cards;
        save.remove(optimalSeq);
        int lastNum = Strategy(m_player, save).findCardsByCount(1).size();
        if(baseNum > lastNum){
            return optimalSeq[0];
        }
    }

    bool hasPlane, hasTriple, hasPair;
    hasPair = hasPlane = hasTriple = false;
    Cards backup = m_cards;
    //判断是否有炸弹
    QVector<Cards> bombArray = findCardType(PlayHand(PlayHand::Hand_Bomb, Card::Card_Begin, 0),false);
    backup.remove(bombArray);
    //判断是否有飞机
    QVector<Cards> planeArray = Strategy(m_player, backup).findCardType(PlayHand(PlayHand::Hand_Plane, Card::Card_Begin, 0),false);
    if(!planeArray.isEmpty()){
        hasPlane = true;
        backup.remove(planeArray);
    }
    //判断是否有三张点数相同的牌
    QVector<Cards> tripleArray = Strategy(m_player, backup).findCardType(PlayHand(PlayHand::Hand_Triple, Card::Card_Begin, 0),false);
    if(!tripleArray.isEmpty()){
        hasTriple = true;
        backup.remove(tripleArray);
    }
    //判断是否有连对
    QVector<Cards> seqPairArray = Strategy(m_player, backup).findCardType(PlayHand(PlayHand::Hand_Seq_Pair, Card::Card_Begin, 0),false);
    if(!seqPairArray.isEmpty()){
        hasPair = true;
        backup.remove(seqPairArray);
    }

    if(hasPair){
        Cards maxPair;
        for(int i = 0; i<seqPairArray.size(); ++i){
            if(seqPairArray[i].cardCount() > maxPair.cardCount()){
                maxPair = seqPairArray[i];
            }
        }
        return maxPair;
    }
    if(hasPlane){
        // 1.尝试出飞机带两个对(要对这两个对进行限制)
        bool twoPairFond = false;
        QVector<Cards> pairArray;
        for(Card::CardPoint point = Card::Card_3; point <= Card::Card_10; point = (Card::CardPoint)(point+1)){
            Cards pair = Strategy(m_player, backup).findSamePointcards(point, 2);
            if(!pair.isEmpty()){
                pairArray.push_back(pair);
                if(pairArray.size() == 2){
                    twoPairFond = true;
                    break;
                }
            }
        }
        if(twoPairFond){
            Cards tmp = planeArray[0];
            tmp.add(pairArray);
            return tmp;
        }
        // 2.尝试出飞机带两个单
        else{
            bool twoSingleFond = false;
            QVector<Cards> singleArray;
            for(Card::CardPoint point = Card::Card_3; point <= Card::Card_10; point = (Card::CardPoint)(point+1)){
                if(backup.pointCount(point) == 1){
                    Cards single = Strategy(m_player, backup).findSamePointcards(point, 1);
                    if(!single.isEmpty()){
                        singleArray.push_back(single);
                        if(singleArray.size() == 2){
                            twoSingleFond = true;
                            break;
                        }
                    }
                }
            }
            if(twoSingleFond){
                Cards tmp = planeArray[0];
                tmp.add(singleArray);
                return tmp;

            }else{// 3.只出一个飞机
                return planeArray[0];
            }
        }
    }
    if(hasTriple){
        if(PlayHand(tripleArray[0]).cardPoint() < Card::Card_A){
            for(Card::CardPoint point = Card::Card_3; point <= Card::Card_A; point = (Card::CardPoint)(point+1)){
                int pointCount = backup.pointCount(point);
                if(pointCount == 1){
                    Cards single = Strategy(m_player, backup).findSamePointcards(point,1);
                    Cards tmp = tripleArray[0];
                    tmp.add(single);
                    return tmp;//返回三代一

                }else if(pointCount == 2){
                    Cards pair = Strategy(m_player, backup).findSamePointcards(point,2);
                    Cards tmp = tripleArray[0];
                    tmp.add(pair);
                    return tmp;//返回三带二

                }
            }
        }
        return tripleArray[0];//返回三张
    }
    // 单牌或者对牌（根据下一个玩家手里的牌进行判断，如：对方剩单则出对）
    Player* newtPlayer = m_player->nextPlayer();
    if(newtPlayer->cards().cardCount() == 1 && m_player->role() != newtPlayer->role()){
        //对方剩单，出对牌
        for(Card::CardPoint point = Card::CardPoint(Card::Card_End-1);point >= Card::Card_3;point = (Card::CardPoint)(point-1)){
            int pointCount = backup.pointCount(point);
            if(pointCount == 1){
                Cards single = Strategy(m_player, backup).findSamePointcards(point, 1);
                return single;
            }else if(pointCount == 2){
                Cards pair = Strategy(m_player, backup).findSamePointcards(point, 2);
                return pair;
            }
        }
    }else{
        for(Card::CardPoint point = Card::Card_3;point < Card::Card_End; point = (Card::CardPoint)(point+1)){
            int pointCount = backup.pointCount(point);
            if(pointCount == 1){
                Cards single = Strategy(m_player, backup).findSamePointcards(point, 1);
                return single;
            }else if(pointCount == 2){
                Cards pair = Strategy(m_player, backup).findSamePointcards(point, 2);
                return pair;
            }
        }
    }
    return Cards();
}

Cards Strategy::getGreaterCards(PlayHand type)
{
    // 1.出牌的玩家和当前玩家不是一伙的,并且当前玩家已经剩很少牌了
    Player* pendPlayer = m_player->pendPlayer();
    if(pendPlayer!=nullptr && pendPlayer->role() != m_player->role() && pendPlayer->cards().cardCount() <= 3){
        QVector<Cards> bombs = findCardsByCount(4);
        for(int i = 0;i<bombs.size();++i){
            if(PlayHand(bombs[i]).canBeat(type)){
                return bombs[i];
            }
        }
        // 检查是否有王炸
        Cards sj = findSamePointcards(Card::Card_SJ,1);
        Cards bj = findSamePointcards(Card::Card_BJ,1);
        if(!sj.isEmpty() && !bj.isEmpty()){
            Cards jokers;
            jokers << sj << bj;
            return jokers;
        }

    }
    // 2.当前玩家和下一个玩家不是一伙的,如果下一家玩家的牌比较少，想办法压住
    Player* nextPlayer = m_player->nextPlayer();
    //将玩家手中的顺子剔除出去
    Cards remain = m_cards;
    remain.remove(Strategy(m_player, remain).pickOptimalSeqSingles());

    //C++新特性的匿名函数与绑定器
    auto beatCard = std::bind([=](const Cards& cards){//匿名函数
        QVector<Cards> beatCardsArray = Strategy(m_player, cards).findCardType(type, true);
        if(!beatCardsArray.isEmpty()){
            if(m_player->role() != nextPlayer->role() && nextPlayer->cards().cardCount() <= 2){
                return beatCardsArray.back();
            }else{
                return beatCardsArray.front();
            }
        }
        return Cards();
    }, std::placeholders::_1);

    Cards cs;
    if(!(cs = beatCard(remain)).isEmpty()){
        return cs;
    }else{
        if(!(cs = beatCard(m_cards)).isEmpty()) return cs;
    }
    return Cards();
}

bool Strategy::whetherToBeat(Cards &cards)
{
    // 没有能够击败对方的牌，直接返回false
    if(cards.isEmpty()){
        return false;
    }
    // 得到出牌玩家的对象（判断是不是跟自己是一伙的）
    Player* pendPlayer = m_player->pendPlayer();
    if(m_player->role() == pendPlayer->role())
    {
        // 如果手里的牌所剩无几并且是一个完整的牌型
        Cards left = m_cards;
        left.remove(cards);
        if(PlayHand(left).handType() != PlayHand::Hand_Unknown){
            return true;
        }
        // 如果要出的牌的最小点数是2，大小王
        Card::CardPoint basePoint = PlayHand(cards).cardPoint();
        if(basePoint == Card::Card_2 || basePoint == Card::Card_BJ || basePoint == Card::Card_SJ){
            return false;
        }
    }else//是对手
    {
        PlayHand myHand(cards);
        // 如果是三个2带1或者带一对、则不出牌（保存实力）
        if((myHand.handType() == PlayHand::Hand_Triple_Single) || (myHand.handType() == PlayHand::Hand_Triple_Pair
                                                                     && myHand.cardPoint() == Card::Card_2)){
            return false;
        }
        // 如果要出的牌是一对2，并且出牌玩家手中的牌数量大于10，自己的牌数量也很多，则不出（保存实力）
        if(myHand.handType() == PlayHand::Hand_Pair && myHand.cardPoint() == Card::Card_2
            && pendPlayer->cards().cardCount() >= 10 && m_player->cards().cardCount() >= 5){
            return false;
        }
    }
    return true;
}

Cards Strategy::findSamePointcards(Card::CardPoint point, int count)
{
    if((count < 1) || (count > 4)){
        return Cards();//非法情况返回空
    }
    // 大小王(最多只有一张)
    if(point == Card::Card_SJ || point == Card::Card_BJ){
        if(count > 1){
            return Cards();//非法情况返回空
        }
        Card card;
        card.setPoint(point);
        card.setSuit(Card::Suit_Begin);
        if(m_cards.contains(card)){
            Cards cards;
            cards.add(card);
            return cards;
        }
        return Cards();
    }
    //非大小王
    int findCount = 0;
    Cards findCards;
    for(int suit = Card::Suit_Begin+1; suit < Card::Suit_End; suit++){
        Card card;
        card.setPoint(point);
        card.setSuit((Card::CardSuit)suit);
        if(m_cards.contains(card)){
            findCount++;
            findCards.add(card);
            if(findCount == count){
                return findCards;
            }
        }
    }
    return Cards();
}

QVector<Cards> Strategy::findCardsByCount(int count)
{
    if((count < 1) || (count > 4)){
        return QVector<Cards>();//非法情况返回空
    }
    QVector<Cards> cardsArray;
    for(Card::CardPoint point = Card::Card_3; point < Card::Card_End; point = (Card::CardPoint)(point+1)){
        if(m_cards.pointCount(point) == count){
            Cards cards;
            cards << findSamePointcards(point, count);
            cardsArray << cards;
        }
    }
    return cardsArray;
}

Cards Strategy::getRangeCards(Card::CardPoint begin, Card::CardPoint end)
{
    Cards findCards;
    for(Card::CardPoint point = begin; point <= end; point = (Card::CardPoint)(point+1)){
        int count = m_cards.pointCount(point);
        Cards cards = findSamePointcards(point, count);
        findCards << cards;
    }
    return findCards;
}

QVector<Cards> Strategy::findCardType(PlayHand hand, bool beat)
{
    PlayHand::HandType type = hand.handType();
    Card::CardPoint point = hand.cardPoint();
    int extra = hand.extra();

    //确定起始点数
    Card::CardPoint beginPoint = beat ? (Card::CardPoint)(point+1) : Card::Card_3;
    switch (type) {
    case PlayHand::Hand_Single:
        return getCards(beginPoint, 1);

    case PlayHand::Hand_Pair:
        return getCards(beginPoint, 2);

    case PlayHand::Hand_Triple:
        return getCards(beginPoint, 3);

    case PlayHand::Hand_Triple_Single:
        return getTripleSingleOrPair(beginPoint, PlayHand::Hand_Single);

    case PlayHand::Hand_Triple_Pair:
        return getTripleSingleOrPair(beginPoint, PlayHand::Hand_Pair);

    case PlayHand::Hand_Plane:
        return getPlane(beginPoint);

    case PlayHand::Hand_Plane_Two_Single:
        return getPlane2SingleOrPair(beginPoint, PlayHand::Hand_Single);

    case PlayHand::Hand_Plane_Two_Pair:
        return getPlane2SingleOrPair(beginPoint, PlayHand::Hand_Pair);

    case PlayHand::Hand_Seq_Pair:
    {
        CardInfo info;
        info.begin = beginPoint;
        info.end = Card::Card_Q;
        info.number = 2;
        info.base = 3;
        info.extra = extra;
        info.beat = beat;
        info.getSeq = &Strategy::getbaseSeqPair;
        return getSeqPairOrSeqSingle(info);
    }

    case PlayHand::Hand_Seq_Single:
    {
        CardInfo info;
        info.begin = beginPoint;
        info.end = Card::Card_10;
        info.number = 1;
        info.base = 5;
        info.extra = extra;
        info.beat = beat;
        info.getSeq = &Strategy::getbaseSeqSingle;
        return getSeqPairOrSeqSingle(info);
    }

    case PlayHand::Hand_Bomb:
        return getBomb(beginPoint);

    default:
        return QVector<Cards>();
    }
}

QVector<Cards> Strategy::getCards(Card::CardPoint point, int number)
{
    QVector<Cards> findCardsArray;
    for(Card::CardPoint pt = point; pt < Card::Card_End; pt = (Card::CardPoint)(pt+1)){
        //尽量不拆分其他的牌型
        if(m_cards.pointCount(pt) == number){
            Cards cards = findSamePointcards(pt, number);
            findCardsArray << cards;
        }
    }
    return findCardsArray;
}

QVector<Cards> Strategy::getTripleSingleOrPair(Card::CardPoint begin, PlayHand::HandType type)
{
    QVector<Cards> findCardArray = getCards(begin, 3);
    //找到点数相同的三张牌
    if(!findCardArray.isEmpty()){
        //将找到的牌删除
        Cards remainCards = m_cards;
        remainCards.remove(findCardArray);
        //找到单牌或成对的牌
        Strategy st(m_player, remainCards);
        QVector<Cards> cardsArray = st.findCardType(PlayHand(type, Card::Card_Begin, 0), false);
        if(!cardsArray.isEmpty()){
            //与点数三张的牌进行组合
            for(int i = 0; i<findCardArray.size(); ++i){
                findCardArray[i].add(cardsArray.at(i));
            }
        }else{
            findCardArray.clear();
        }

    }
    //返回组合的牌
    return findCardArray;
}

QVector<Cards> Strategy::getPlane(Card::CardPoint begin)
{
    QVector<Cards> findCardArray;
    for(Card::CardPoint point = begin; point <= Card::Card_K; point = (Card::CardPoint)(point+1)){
        // 根据点数和数量进行搜索
        Cards preCards = findSamePointcards(point, 3);
        Cards nexCard = findSamePointcards((Card::CardPoint)(point + 1), 3);
        if(!preCards.isEmpty() && !nexCard.isEmpty()){
            Cards tmp;
            tmp << preCards << nexCard;
            preCards.add(nexCard);
            findCardArray << tmp;
        }
    }
    return findCardArray;
}

QVector<Cards> Strategy::getPlane2SingleOrPair(Card::CardPoint begin, PlayHand::HandType type)
{
    QVector<Cards> findCardArray = getPlane(begin);
    //找飞机
    if(!findCardArray.isEmpty()){
        //将找到的牌删除
        Cards remainCards = m_cards;
        remainCards.remove(findCardArray);
        //找到单牌或成对的牌
        Strategy st(m_player, remainCards);
        QVector<Cards> cardsArray = st.findCardType(PlayHand(type, Card::Card_Begin, 0), false);
        if(cardsArray.size() >= 2){
            //与飞机进行组合
            for(int i = 0; i<findCardArray.size();++i){
                Cards tmp;
                tmp << cardsArray[0] << cardsArray[1];
                findCardArray[i].add(tmp);
            }
        }
    }
    else{
        findCardArray.clear();
    }
    //返回组合的牌
    return findCardArray;
}

QVector<Cards> Strategy::getSeqPairOrSeqSingle(CardInfo& info)
{
    QVector<Cards> findCardsArray;
    if(info.beat){
        for(Card::CardPoint point = info.begin; point <= info.end; point = (Card::CardPoint)(point+1)){
            bool found = true;
            Cards seqCards;
            for(int i = 0; i<info.extra; ++i){
                // 基于点数和数量进行搜索
                Cards cards = findSamePointcards((Card::CardPoint)(point + i), info.number);
                if(cards.isEmpty() || (point + info.extra >= Card::Card_2)){
                    found = false;
                    seqCards.clear();
                    break;
                }
                else{
                    seqCards << cards;
                }
            }
            if(found){
                findCardsArray << seqCards;
                return findCardsArray;
            }
        }
    }else{
        for(Card::CardPoint point = info.begin; point <= info.end; point = (Card::CardPoint)(point+1)){
            //寻找最基础的连对或顺子
            Cards baseSeq = (this->*info.getSeq)(point);
            if(baseSeq.isEmpty()){ continue;}
            findCardsArray << baseSeq;

            int followd = info.base;
            Cards alreadyFollowedCards;//存储后续找到的满足条件的连对
            while(true){
                //新的起始点数
                Card::CardPoint followedPoint = Card::CardPoint(point+followd);
                //判断是否超出上限
                if(followedPoint >= Card::Card_2){
                    break;
                }
                Cards followedCards = findSamePointcards(followedPoint,info.number);
                if(followedCards.isEmpty()){
                    break;
                }else{
                    alreadyFollowedCards << followedCards;
                    Cards newSeq = baseSeq;
                    newSeq << alreadyFollowedCards;
                    findCardsArray << newSeq;
                    followd++;
                }
            }
        }
    }
    return findCardsArray;
}

Cards Strategy::getbaseSeqPair(Card::CardPoint point)
{
    Cards card0 = findSamePointcards(point, 2);
    Cards card1 = findSamePointcards((Card::CardPoint)(point+1), 2);
    Cards card2 = findSamePointcards((Card::CardPoint)(point+2), 2);
    Cards baseSeq;
    if(!card0.isEmpty() && !card1.isEmpty() && !card2.isEmpty()){
        baseSeq << card0 << card1 << card2;
    }
    return baseSeq;
}

Cards Strategy::getbaseSeqSingle(Card::CardPoint point)
{
    Cards card0 = findSamePointcards(point, 1);
    Cards card1 = findSamePointcards((Card::CardPoint)(point+1), 1);
    Cards card2 = findSamePointcards((Card::CardPoint)(point+2), 1);
    Cards card3 = findSamePointcards((Card::CardPoint)(point+3), 1);
    Cards card4 = findSamePointcards((Card::CardPoint)(point+4), 1);
    Cards baseSeq;
    if(!card0.isEmpty() && !card1.isEmpty() && !card2.isEmpty() && !card3.isEmpty() &&!card4.isEmpty()){
        baseSeq << card0 << card1 << card2 << card3 << card4;
    }
    return baseSeq;
}

QVector<Cards> Strategy::getBomb(Card::CardPoint begin)
{
    QVector<Cards> findCardsArray;
    for(Card::CardPoint point = begin; point < Card::Card_End; point = (Card::CardPoint)(point+1)){
        Cards cs = findSamePointcards(point, 4);
        if(!cs.isEmpty()){
            findCardsArray << cs;
        }
    }
    return findCardsArray;
}

void Strategy::pickSeqSingle(QVector<QVector<Cards>>& allSeqRecord, const QVector<Cards>& seqSingle, const Cards &cards)
{
    // 1.得到所有顺子的组合
    QVector<Cards> allSeq = Strategy(m_player, cards).findCardType(PlayHand(PlayHand::Hand_Seq_Single,Card::Card_Begin, 0), false);
    if(allSeq.isEmpty()){
        //结束递归，将满足条件的顺子传递给调用者
        allSeqRecord << seqSingle;

    }else{//2.对顺子进行筛选
        Cards saveCards = cards;
        for(int i = 0; i<allSeq.size();++i){
            //将顺子取出
            Cards aScheme = allSeq.at(i);
            //将顺子从用户手中删除
            Cards tmp = saveCards;
            tmp.remove(aScheme);

            QVector<Cards> seqArray = seqSingle;
            seqArray << aScheme;

            //检测还有没有其他的顺子
            //seqArray存储的是一轮循环中得到的多个顺子
            //allSeqRecord存储的是多轮循环中得到的所有顺子
            pickSeqSingle(allSeqRecord, seqArray, tmp);
        }
    }
}

QVector<Cards> Strategy::pickOptimalSeqSingles()
{
    QVector<QVector<Cards>> seqRecord;
    QVector<Cards> seqSingles;
    //先把炸弹和三张剔除（避免因小失大）
    Cards save = m_cards;
    save.remove(findCardsByCount(4));
    save.remove(findCardsByCount(3));
    pickSeqSingle(seqRecord, seqSingles, save);
    if(seqRecord.isEmpty()){//没有顺子
        return QVector<Cards>();
    }
    // 遍历容器，找到使得手里剩下最少的牌的顺子
    QMap<int, int> seqMarks;
    for(int i = 0;i<seqRecord.size();++i){
        Cards backupCards = m_cards;
        QVector<Cards> seqArray = seqRecord[i];
        backupCards.remove(seqArray);
        //只判断剩下的单牌数量
        QVector<Cards> singleArray = Strategy(m_player, backupCards).findCardsByCount(1);
        CardList cardsList;
        for(int j = 0; j < singleArray.size(); ++j){
            cardsList << singleArray[j].toCardList();
        }
        //找点数相对较大一点点的顺子
        int mark = 0;
        for(int k = 0; k < cardsList.size(); ++k){
            mark += (cardsList[k].point() + 15);
        }
        seqMarks.insert(i,mark);
    }
    //遍历map,寻找最小的mark值
    int value = 0;
    int comMark = 1000;
    auto it = seqMarks.constBegin();
    for(;it!=seqMarks.constEnd();++it){
        if(comMark >= it.value()){
            comMark = it.value();
            value = it.key();
        }
    }
    return seqRecord[value];
}
